SimpleParacol = function(controllerUrl, extension) {

    var paracol = Paracol(DefaultParacolBasicMessageFilters.addFieldsAsObject, {
        getNameValuePairFromField: function(field) {
            switch (field.getAttribute("otype")) {
                case "NumberField":
                    return [field.name, parseFloat(field.value)];
                case "BooleanField":
                    return [field.name, field.value == "on"];
                default:
                    return this._next_.getNameValuePairFromField(field);
            }
        }

    });
    paracol.dispatcherUrl = controllerUrl;

    var extensionPoints = ParacolUtils.makeMixable({
        /**
         * Extension point
         */
        updateHeader: function(msg) {
            var headerElem = findHeaderElem(msg.context.page);
            if (headerElem) {
                headerElem.firstChild.textContent = msg.context.name;
            }
        },

        /**
         * Extension point
         */
        updateFieldValue: function(msg, fieldName, fieldElem, value) {
            fieldElem.setAttribute("otype", msg.fieldsObject[fieldName].otype);
            if (msg.fieldsObject[fieldName].otype == "BooleanField") {
                if (value) {
                    fieldElem.setAttribute("checked", "");
                }
            } else {
                fieldElem.value = value;
            }
        },

        /**
         * Extension point
         */
        updateFieldMetaData: function(msg, fieldName) {
            var commonMetaData = msg.fieldsObject[fieldName].CommonMetaData;
            var label;
            if (commonMetaData) {
                label = commonMetaData.title;
            } else {
                label = msg.fieldsObject[fieldName].label;
            }
            findLabel(msg.context.page, fieldName).textContent = label;
        },

        /**
         * Extension point
         */
        updateErrorMessage: function(msg, fieldName) {
            var errorElem = findErrorElem(msg.context.page, fieldName);
            var validationInfo = msg.fieldsObject[fieldName].ValidationInfo;
            if (validationInfo && !validationInfo.isValid) {
                addClass(errorElem, "visibleElement");
                errorElem.firstChild.textContent = validationInfo.message;
            } else {
                removeClass(errorElem, "visibleElement");
            }
        },

        /**
         * Extension point
         */
        updateFieldOptions: function(msg, fieldName, fieldElem, value) {
            var listItems = msg.fieldsObject[fieldName].ListItems;
            if (listItems) {
                // remove the old options
                while (fieldElem.hasChildNodes()) {
                    fieldElem.removeChild(fieldElem.firstChild)
                }

                for (i = 0; i < listItems.data.length; i++) {
                    var item = listItems.data[i];
                    var opt = document.createElement("option");
                    opt.setAttribute("value", item[0]);

                    if (item[0] == value) {
                        opt.setAttribute("selected", "");
                    }

                    var t = document.createTextNode(item[1]);
                    opt.appendChild(t);
                    fieldElem.appendChild(opt);
                }
            }
        },

        /**
         * Extension point
         */
        showPanel: function (panelId) {
            var abbrev = page2abbrev[panelId] || panelId;
            addClass(document.getElementById(abbrev), "visibleElement");
        },

        /**
         * Extension point
         */
        hidePanel: function(panelId) {
            if (panelId) {
                var abbrev = page2abbrev[panelId] || panelId;
                removeClass(document.getElementById(abbrev), "visibleElement");
            }
        },

        /**
         * Extension point
         */
        handleResult: function(msg) {
            this.showPanel("ResultPanel");
            document.getElementById("result").firstChild.textContent = msg.content;
        },

        /**
         * Extension point
         */
        handleEnd: function(msg) {
            this.hidePanel("ResultPanel");
        }
    });

    // mix in the extensions
    extensionPoints.MixWith(extension);

    var pageId;
    var editedFields;
    var page2abbrev = {};
    var abbrev2page = {};
    var pageFields = {};

    var messageReceiver = {
        onEditCookie: function(msg) {
            editedFields = [];
            extensionPoints.hidePanel(pageId);
            pageId = msg.context.page;
            extensionPoints.showPanel(pageId);
            extensionPoints.updateHeader(msg);
            updateFields(msg, pageFields[pageId]);
        },
        onResult: function(msg) {
            extensionPoints.hidePanel(pageId);
            pageId = null;
            extensionPoints.handleResult(msg);
        },
        onEnd: function(msg) {
            extensionPoints.handleEnd(msg);
        }

    };

    function findField(pageId, fieldName) {
        var abbrev = page2abbrev[pageId];
        return document.getElementById(abbrev + '_' + fieldName);
    }

    function findLabel(pageId, fieldName) {
        var abbrev = page2abbrev[pageId];
        return document.getElementById(abbrev + '_' + fieldName + "_label").firstChild;
    }

    function findErrorElem(pageId, fieldName) {
        var abbrev = page2abbrev[pageId];
        return document.getElementById(abbrev + '_' + fieldName + "_error");
    }

    function findHeaderElem(pageId) {
        var abbrev = page2abbrev[pageId];
        return document.getElementById(abbrev + "_header");
    }

    function addClass(elem, cls) {
        var classAttr = elem.getAttribute("class");
        if (classAttr) {
            elem.setAttribute("class", classAttr + " " + cls);
        } else {
            elem.setAttribute("class", cls);
        }
    }

    function removeClass(elem, remCls) {
        var classAttr = elem.getAttribute("class");
        if (classAttr) {
            var classes = classAttr.split(' ');
            classAttr = "";
            for (c = 0; c < classes.length; c++) {
                if (classes[c] != remCls) {
                    if (classAttr == "")
                        classAttr = classes[c];
                    else
                        classAttr = classAttr + " " + classes[c];
                }
            }
            elem.setAttribute("class", classAttr);
        }
    }


    function updateField(msg, fieldName) {
        var value = msg.fieldsObject[fieldName].value;

        var fieldElem = findField(msg.context.page, fieldName);
        editedFields[editedFields.length] = fieldElem;

        extensionPoints.updateFieldValue(msg, fieldName, fieldElem, value);

        extensionPoints.updateFieldMetaData(msg, fieldName);

        extensionPoints.updateErrorMessage(msg, fieldName);

        extensionPoints.updateFieldOptions(msg, fieldName, fieldElem, value);
    }

    function updateFields(msg, fieldNames) {
        for (f = 0; f < fieldNames.length; f++) {
            updateField(msg, fieldNames[f]);
        }
    }

    return {
        registerPage: function(pageId, abbrev, fieldNames) {
            page2abbrev[pageId] = abbrev;
            abbrev2page[abbrev] = pageId;
            pageFields[pageId] = fieldNames;
        },

        consume: function() {
            paracol.consumeCookie(editedFields, messageReceiver);
        },

        unconsume: function() {
            paracol.unconsumeCookie(messageReceiver);
        },

        closeFlow: function() {
            paracol.closeFlow(messageReceiver);
        },

        load: function() {
            paracol.refresh(messageReceiver);
        }
    }
};